# 1. 选择文件夹
# 2. 输入文件大小 默认 500kb
# 3. 选择图片格式 默认 jpg 可选择 png
# 4. 选择输出文件夹
# 5. 压缩
# 6. 完成
import os
import threading
import tkinter
from tkinter import ttk, Tk, Menu, filedialog, Label, Text, END, messagebox

from PIL import Image

file_list = []
image_size = 500
naming_rules = "%d"  # 命名规则
# 五毛买的
char_ct = '☑'  # 复选框选中标识符
chat_cf = '□'  # 复选框未选中标识符
submit_text = "执行"


class STATUS:  # 同步状态
    WAIT_MATCHING = "待处理"
    FAIL = "处理失败"
    SUCCESS = "处理成功"


class ImageFile:
    select_status = None
    i = None
    fileName = None
    path = None
    file_status = None
    file_size = None
    file_path = None
    msg = ""

    def __init__(self, i, path, file_name, file_status, file_size, file_path):
        self.select_status = True
        self.i = i
        self.fileName = file_name
        self.path = path
        self.file_status = file_status
        self.file_size = file_size
        self.file_path = file_path


class my_gui:
    def __init__(self, init_window):
        self.naming_rules_Label = None
        self.image_size_Text = None
        self.image_size_Label = None
        self.tree = None
        self.env_var = None
        self.file_menu = None
        self.menu_bar = None
        self.mode_menu = None
        self.init_window = init_window

    def set_init_window(self):
        #self.init_window.iconbitmap(r'img\icon.ico')
        self.init_window.title("检察院电子卷处理工具-鲍哲")
        self.init_window.geometry('1068x681')
        self.init_window.attributes("-alpha", 0.9)  # 虚化，值越小虚化程度越高
        # menu定义
        self.menu_bar = Menu(self.init_window)
        self.init_window.config(menu=self.menu_bar)
        # 文件导入
        self.file_import()
        # 文件列表
        self.file_list_grid()

        # 参数设置
        self.image_size_Label = Label(self.init_window, text="图片大小（kb）")
        self.image_size_Label.grid(row=2, column=11, sticky=tkinter.W)
        self.image_size_Text = Text(self.init_window, width=30, height=1)
        self.image_size_Text.grid(row=2, column=12, sticky=tkinter.W)
        self.image_size_Text.insert(END, str(image_size))

        self.naming_rules_Label = Label(self.init_window, text="命名规则")
        self.naming_rules_Label.grid(row=3, column=11, sticky=tkinter.W)
        self.naming_rules_Text = Text(self.init_window, width=30, height=1)
        self.naming_rules_Text.grid(row=3, column=12, sticky=tkinter.W)
        self.naming_rules_Text.insert(END, naming_rules)

        self.start_number_Label = Label(self.init_window, text="起始编号（数值）")
        self.start_number_Label.grid(row=4, column=11, sticky=tkinter.W)
        self.start_number_Text = Text(self.init_window, width=30, height=1)
        self.start_number_Text.grid(row=4, column=12, sticky=tkinter.W)
        self.start_number_Text.insert(END, str(10000))

        self.output_path_Label = Label(self.init_window, text="输出路径")
        self.output_path_Label.grid(row=5, column=11, sticky=tkinter.W)
        self.output_path_Text = Text(self.init_window, width=30, height=1)
        self.output_path_Text.grid(row=5, column=12, sticky=tkinter.W)
        self.output_path_Text.insert(END, "D:\\output")
        self.output_path_button = tkinter.Button(self.init_window, text="选择文件夹", command=self.select_out_dir)
        self.output_path_button.grid(row=5, column=13, sticky=tkinter.W)

        self.start_compress_button = tkinter.Button(self.init_window, text=submit_text, height=2,
                                                    font=("微软雅黑", 20, "bold"),
                                                    command=lambda: thread_it(self.start_compress))
        self.start_compress_button.grid(row=7, column=11, columnspan=3, sticky=tkinter.NSEW)

        self.output_path_Label = Label(self.init_window, text="对党忠诚 服务人民",
                                       font=("楷体", 24, "bold"))
        self.output_path_Label.grid(row=8, column=11, columnspan=3, sticky=tkinter.NSEW)
        self.output_path_Label = Label(self.init_window, text="执法公正 纪律严明",
                                       font=("楷体", 24, "bold"))
        self.output_path_Label.grid(row=9, column=11, columnspan=3, sticky=tkinter.NSEW)

    def update_start_compress_status(self, status=False):
        if status:
            self.start_compress_button["text"] = "执行"
            self.start_compress_button["state"] = "normal"
        elif self.start_compress_button["text"] == "执行":
            self.start_compress_button["text"] = "执行中..."
            self.start_compress_button["state"] = "disabled"

    def file_list_grid(self):
        """ 文件列表 """
        # (1)创建样式
        style = ttk.Style()
        style.configure("Treeview", rowheight=20)  # 设置行高
        style.configure("Treeview.Heading", font=('微软雅黑', 11, 'bold'))  # 设置表头字体

        # (2)创建 Treeview 控件，设置高度为 20 行
        self.tree = ttk.Treeview(self.init_window, height=30, style="Treeview", show="headings")
        self.tree.grid(row=2, column=0, rowspan=10, columnspan=10, sticky="nsew")

        # (3)定义列名
        self.tree["columns"] = ("checkbox", "i", "file_name", "file_status", "file_size", "msg")

        # (4)设置列的标题名称，anchor可设置对其方式：居中(center)/左对齐(w)/右对齐(e)，无anchor参数时，标题名称默认居中
        self.tree.heading('checkbox', text='选择')
        self.tree.heading("i", text="序号", anchor="e")
        self.tree.heading("file_name", text="文件名称")
        self.tree.heading("file_status", text="状态")
        self.tree.heading("file_size", text="大小")
        self.tree.heading("msg", text="错误描述")

        # (5)设置列宽度(像素)，无anchor参数时，列表中的数据除(#0)外其余都是默认左对齐
        self.tree.column("checkbox", width=55, anchor="center")
        self.tree.column("i", width=55, anchor="e")
        self.tree.column("file_name", width=150, anchor="w")
        self.tree.column("file_status", width=100, anchor="center")
        self.tree.column("file_size", width=50)
        self.tree.column("msg", width=200, anchor="w")

        # (6)创建垂直滚动条并将其关联到Treeview
        vsb = ttk.Scrollbar(self.init_window, orient="vertical", command=self.tree.yview)
        vsb.grid(row=2, column=10, rowspan=10, sticky="ns")
        # 将滚动条关联到 Treeview
        self.tree.configure(yscrollcommand=vsb.set)

    def file_import(self):
        self.file_menu = Menu(self.menu_bar, tearoff=0)
        self.menu_bar.add_command(label="文件导入", command=self.select_file)
        # self.menu_bar.add_command(label="开始压缩", command=self.start_compress)

    def fill_data(self, data_list: []):
        self.tree.delete(*self.tree.get_children())  # 清空文件列表
        for data in data_list:
            self.tree.insert("", "end",
                             values=(char_ct, data.i, data.fileName, data.file_status, data.file_size, data.msg))

    def select_file(self):
        # 重置 file_list
        file_list.clear()
        # 定义一个函数来加载文件列表并显示详细信息
        folder_path = filedialog.askdirectory()
        if folder_path:
            files = os.listdir(folder_path)
            for i, fileName in enumerate(files, start=1):
                # 判断是否为图片jpg、png、jpeg 忽略大小写
                if not fileName.lower().endswith(('.jpg', '.png', '.jpeg')):
                    continue
                file_path = os.path.join(folder_path, fileName)
                file_size = os.path.getsize(file_path)
                file_status = STATUS.WAIT_MATCHING
                image_file = ImageFile(i=i, file_name=fileName, file_status=file_status, path=file_path,
                                       file_size=file_size, file_path=file_path)
                file_list.append(image_file)
            if len(file_list) > 0:
                self.fill_data(file_list)

    def select_out_dir(self):
        folder_path = filedialog.askdirectory()
        self.output_path_Text.delete(1.0, END)
        self.output_path_Text.insert(END, folder_path)

    def start_compress(self):
        self.update_start_compress_status(False)
        # 获取参数
        image_size = int(self.image_size_Text.get(1.0, END))  # 图片大小
        naming_rules = self.naming_rules_Text.get(1.0, END).replace("\n", "")  # 命名规则
        start_number = int(self.start_number_Text.get(1.0, END))  # 起始编号
        output_path = self.output_path_Text.get(1.0, END).replace("\n", "")  # 输出路径
        check = self.check(image_size, naming_rules, start_number, output_path)
        if not check:
            return
        for i in range(0, len(file_list)):
            self.compress(file_list[i], image_size, naming_rules, start_number, output_path, i + 1)
        self.fill_data(file_list)
        messagebox.showinfo("提示信息", "执行完成！")
        self.update_start_compress_status(True)

    def check(self, image_size, naming_rules, start_number, output_path):
        if not image_size:
            self.popup_window("请确认这个文件夹是否已经同步")
            return False
        if not naming_rules or not naming_rules.strip() or not naming_rules.strip().find("%d") > -1:
            self.popup_window("请确认命名规则是否正确")
            return False
        if not start_number:
            self.popup_window("请确认起始编号是否正确")
            return False
        if not output_path or not naming_rules.strip():
            self.popup_window("请确认输出路径是否正确")
            return False
        return True

    def popup_window(self, msg):
        """
        弹窗提示
        :param msg:
        :param path:
        :return:
        """
        win = tkinter.Toplevel()
        win.title("提示")
        # 禁止用户调整窗口大小
        win.resizable(False, False)
        # 在屏幕中央
        win.geometry("300x100+%d+%d" % ((self.init_window.winfo_screenwidth() - 300) / 2,
                                        (self.init_window.winfo_screenheight() - 100) / 2))
        tkinter.Label(win, text=msg).pack()
        tkinter.Button(win, text="确定", command=win.destroy).pack()

    def compress(self, item: ImageFile, target_size, naming_rule, start_number, output_path, i):
        """
        压缩
        :param i:  序号
        :param item:    文件
        :param target_size:  限制图片大小kb
        :param naming_rule:    命名规则
        :param start_number:    起始编号
        :param output_path: 输出路径
        :return:
        """
        # 1. 压缩
        if item.file_path:
            # image = Image.open(item.file_path)
            # 获取文件后缀
            file_suffix = ".jpg"  # os.path.splitext(item.fileName)[1]
            # 2. 命名
            file_name = naming_rule % (start_number + i) + file_suffix
            # 3. 保存路径
            output_path = os.path.join(output_path, file_name)
            compression_ratio = 95
            with Image.open(item.file_path) as img:
                ys1(os.path.getsize(item.file_path) / 1024, target_size * 0.8, target_size, compression_ratio,
                    img, output_path)
            # 4. 更新状态
            item.file_status = STATUS.SUCCESS


def ys1(current_size, target_size_low, target_size_high, compression_ratio, img, output_path):
    if current_size < target_size_high:
        ys2(img, output_path)
        return
    while current_size < target_size_low or current_size > target_size_high:
        if current_size < target_size_low:
            compression_ratio += 1
        elif current_size > target_size_high:
            compression_ratio -= 1

        # 对图片进行压缩
        rgb_img = img.convert("RGB")
        rgb_img.save(output_path, quality=compression_ratio, optimize=True)

        current_size = os.path.getsize(output_path) / 1024


def ys2(img, output_path):
    # 对图片进行压缩
    rgb_img = img.convert("RGB")
    rgb_img.save(output_path, optimize=True)


def ys3(target_size_high, compression_ratio, img, output_path):
    # 获取原始图片大小
    original_size = img.size
    # 计算压缩比例
    if target_size_high * 1024 < original_size[0] * original_size[1]:
        compression_ratio = int(target_size_high * 1024 * 3 / (original_size[0] * original_size[1]) * 100)
    # 对图片进行压缩
    rgb_img = img.convert("RGB")
    rgb_img.save(output_path, quality=compression_ratio, optimize=True)


def calculate_compression_ratio(original_size, compressed_size):
    if original_size == 0:
        return "原始文件大小不能为0"

    compression_ratio = (original_size - compressed_size) / original_size
    return compression_ratio


def thread_it(func, *args):
    # 创建线程
    t = threading.Thread(target=func, args=args)
    # 守护线程
    t.setDaemon(True)
    # 启动
    t.start()


def gui_start():
    gtk = Tk()  # 实例化出一个父窗口
    # 禁止用户调整窗口大小
    gtk.resizable(False, False)
    main_wnd = my_gui(gtk)
    # 设置根窗口默认属性
    main_wnd.set_init_window()
    gtk.mainloop()  # 父窗口进入事件循环，可以理解为保持窗口运行，否则界面不展示


class login_window:
    def __init__(self, init_window):
        self.password_entry = None
        self.init_window = init_window
        super().__init__()
        self.init_window.title("登录")
        self.init_window.geometry("400x200")
        self.password = "110..++6"  # 正确的密码
        self.center_window()

    def center_window(self):
        #self.init_window.iconbitmap(r'img\icon.ico')
        label = tkinter.Label(self.init_window, text="请输入密码:", font=("宋体", 14, "bold"), foreground="red")
        label.pack(pady=10)
        self.password_entry = tkinter.Entry(self.init_window, show="*", font=("宋体", 12, "bold"))
        self.password_entry.pack(pady=10)
        button = tkinter.Button(self.init_window, text="登录", command=self.login, font=("宋体", 12, "bold"))
        button.pack(pady=10)
        # Calculate the position to center the window
        screen_width = self.init_window.winfo_screenwidth()
        screen_height = self.init_window.winfo_screenheight()
        window_width = 400
        window_height = 200
        x = (screen_width - window_width) // 2
        y = (screen_height - window_height) // 2
        self.init_window.geometry(f"400x200+{x}+{y}")
        self.init_window.bind('<Return>', lambda event: self.login())

    def login(self):
        password = self.password_entry.get()
        if password == self.password:
            messagebox.showinfo("提示信息", "遵守保密规定")
            # 密码正确，打开主界面
            self.init_window.destroy()
            gui_start()
        else:
            # 密码错误，显示错误消息
            messagebox.showerror("错误", "密码错误")


def gui_login_start():
    gtk = Tk()  # 实例化出一个父窗口
    # 禁止用户调整窗口大小
    gtk.resizable(False, False)
    login_window(gtk)
    gtk.mainloop()  # 父窗口进入事件循环，可以理解为保持窗口运行，否则界面不展示


gui_login_start()
